package template;

//the list of imports
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import logist.Measures;
import logist.behavior.AuctionBehavior;
import logist.agent.Agent;
import logist.simulation.Vehicle;
import logist.plan.Action;
import logist.plan.Plan;
import logist.task.Task;
import logist.task.TaskDistribution;
import logist.task.TaskSet;
import logist.topology.Topology;
import logist.topology.Topology.City;

/**
 * A very simple auction agent that assigns all tasks to its first vehicle and
 * handles them sequentially.
 * 
 */
@SuppressWarnings("unused")
public class AuctionAgent implements AuctionBehavior {

	private Topology topology;
	private TaskDistribution distribution;
	private Agent agent;
	private Random random;
	private Vehicle vehicle;
	private City currentCity;
	private Assignment currentAssignment;
	private Assignment futureAssignment;
	private int auctionCounter;
	
	@Override
	public void setup(Topology topology, TaskDistribution distribution,
			Agent agent) {

		this.topology = topology;
		this.distribution = distribution;
		this.agent = agent;
		this.vehicle = agent.vehicles().get(0);
		this.currentCity = vehicle.homeCity();
		this.auctionCounter = 0;
		
		long seed = -9019554669489983951L * currentCity.hashCode() * agent.id();
		this.random = new Random(seed);

	}

	@Override
	public void auctionResult(Task previous, int winner, Long[] bids) {
		System.out.println("auctionResult taskid " + previous.id);
		for (int i=0;i<bids.length; i++){
			System.out.println("i "+ i + " bid " + bids[i]);
		}
		if (winner == agent.id()) {			
			//currentCity = previous.deliveryCity;
			currentAssignment = futureAssignment;
			System.out.println("Task: " + previous.toString());
			List<Plan> plans = currentAssignment.getPlans();
			for (int i = 0; i < plans.size(); i++) {
				System.out.println("Vehicle " + i + ": " + plans.get(i).toString());
			}
			System.out.println("total cost = " + currentAssignment.getTotalCost());
		
		}
	}
	

	@Override
	public Long askPrice(Task task) {
		this.auctionCounter++;
		if (this.auctionCounter == 1){
			this.currentAssignment = new Assignment(this.agent.vehicles(), this.agent.getTasks());
		}
		this.futureAssignment = evaluateTask(task);
		
		double marginalCost = futureAssignment.getTotalCost() - currentAssignment.getTotalCost();
		
		//System.out.println("marginal cost: " + marginalCost);
		//marginalCost = (marginalCost > 0) ? marginalCost : 0;

		double ratio = 1.0 + (random.nextDouble() * 0.05 * task.id);
		double bid = ratio * marginalCost;

		return (long) Math.round(bid);
	}
	
	private Assignment evaluateTask(Task task) {
		Assignment A = currentAssignment.addNewAuctionedTask(task);
		int count = 0, convergent = 0;
		double oldCost = 0.0;
		while (count++ < 10000) {
			ArrayList<Assignment> N = A.chooseNeighbours();
			A = A.localChoice(N);
//			List<Plan> plans = A.getPlans();
//			for (int i = 0; i < plans.size(); i++) {
//				System.out.println("Vehicle " + i + ": " + plans.get(i).toString() + ", cost per km: " + vehicles.get(i).costPerKm());
//			}
//			System.out.println("total cost = " + A.getTotalCost());
			if (A.getTotalCost() == oldCost) {
				if (++convergent >= 100)
					break;
			}
			else
				convergent = 0;
			oldCost = A.getTotalCost();
		}
		return A;
	}

	@Override
	public List<Plan> plan(List<Vehicle> vehicles, TaskSet tasks) {
		
//		System.out.println("Agent " + agent.id() + " has tasks " + tasks);

		//Plan planVehicle1 = naivePlan(vehicle, tasks);

		List<Plan> plans = centralizedPlan(vehicles, tasks);
//		List<Plan> plans = centralizedPlan(vehicles, tasks);
		System.out.println("centralized size "+plans.size() + "  current size " + this.currentAssignment.getPlans().size() );
		
		
//		plans.add(planVehicle1);
//		while (plans.size() < vehicles.size())
//			plans.add(Plan.EMPTY);

		return plans;
	}

	private Plan naivePlan(Vehicle vehicle, TaskSet tasks) {
		City current = vehicle.getCurrentCity();
		Plan plan = new Plan(current);

		for (Task task : tasks) {
			// move: current city => pickup location
			for (City city : current.pathTo(task.pickupCity))
				plan.appendMove(city);

			plan.appendPickup(task);

			// move: pickup location => delivery location
			for (City city : task.path())
				plan.appendMove(city);

			plan.appendDelivery(task);

			// set current city
			current = task.deliveryCity;
		}
		return plan;
	}
	
	private List<Plan> centralizedPlan(List<Vehicle> vehicles, TaskSet tasks) {
		Assignment A = new Assignment(vehicles, tasks);
		int count = 0, convergent = 0;
		double oldCost = 0.0;
		while (count++ < 10000) {
			ArrayList<Assignment> N = A.chooseNeighbours();
			A = A.localChoice(N);
			List<Plan> plans = A.getPlans();
			for (int i = 0; i < plans.size(); i++) {
				System.out.println("Vehicle " + i + ": " + plans.get(i).toString() + ", cost per km: " + vehicles.get(i).costPerKm());
			}
			System.out.println("total cost = " + A.getTotalCost());
			if (A.getTotalCost() == oldCost) {
				if (++convergent >= 100)
					break;
			}
			else
				convergent = 0;
			oldCost = A.getTotalCost();
		}
		
//		System.out.println("===============================" + tasks.size() );
		if (currentAssignment.getTotalCost() < A.getTotalCost()) {
//			List<Plan> plans = A.getPlans();
//			for (int i = 0; i < plans.size(); i++) {
//				System.out.println("Vehicle " + i  + plans.get(i).toString());
//			}
//			
//			System.out.println("===============================");
//			System.out.println(tasks);

			
			List<Plan> plans = currentAssignment.getPlansNewTaskSet(tasks);
//			for (int i = 0; i < plans.size(); i++) {
//				//for ()
//				 Iterator iter = l.iterator();
//				 while(iter.hasNext()){
//				  String str = (String) iter.next();
//				  System.out.println(str);
//				 }
 				
//				for (Iterator<Action> iter = plans.get(i).iterator(); iter.hasNext();) {
//					System.out.println(iter.next());
//					Action temp = (Action)iter.next();
//					temp.
//				}
//				System.out.println("Vehicle " + i  + plans.get(i).toString());
			
			
			return plans;
		}
		return A.getPlans();
	}
	
}
